use std::{time::{Instant, Duration}, collections::HashMap};

use chrono::Utc;
use tonic::Status;
use uuid::Uuid;

use crate::gen::{entities::{Account, MetaData, Name, Restaurant, Rider, Order}, mini_mulazim::{KeysResponse}};

/**
 * @author Dream-Lab مۇختەرجان مەخمۇت
 * @email ug-project@outlook.com
 * @create date 2022-07-23 15:59:33
 * @modify date 2022-07-23 15:59:33
 * @desc [description]
*/

impl MetaData {
    pub fn new_with_default_value() -> MetaData {
        MetaData { created_at: Utc::now().timestamp(), updated_at: Utc::now().timestamp(), uuid: Uuid::new_v4().to_string(), }
    }
}

impl Name {
    pub fn new(ug: &str, zh: &str) -> Name {
        Name { ug: ug.into(), zh: zh.into(), meta: Some(MetaData::new_with_default_value()), }
    }
}

pub trait IntoTonicResponse<T, E> {
    fn into_tonic_response(self) -> std::result::Result<tonic::Response<T>, E>;
}

impl<T> IntoTonicResponse<T, tonic::Status> for Option<T> {
    fn into_tonic_response(self) -> std::result::Result<tonic::Response<T>, tonic::Status> {
        if let Some(me) = self {
            Ok(tonic::Response::new(me))
        } else {
            Err(Status::not_found("Data not found."))
        }
    }
}
pub trait ToOption<T> {
    fn into_option(self) -> Option<T>;
}
impl<T> ToOption<T> for T {
    fn into_option(self) -> Option<T> {
        Some(self)
    }
}

pub trait ToKeysResponse {
    fn into_keys_response(self) -> KeysResponse;
}

impl ToKeysResponse for Vec<String> {
    fn into_keys_response(self) -> KeysResponse {
        KeysResponse { keys: self }
    }
}

pub trait GetEntityKey {
    fn key(&self) -> String;
}

impl GetEntityKey for Account {
    fn key(&self) -> String {
        self.meta.clone().unwrap().uuid
    }
}

impl GetEntityKey for Restaurant {
    fn key(&self) -> String {
        self.meta.clone().unwrap().uuid
    }
}
impl GetEntityKey for Rider {
    fn key(&self) -> String {
        self.meta.clone().unwrap().uuid
    }
}
impl GetEntityKey for Order {
    fn key(&self) -> String {
        self.meta.clone().unwrap().uuid
    }
}

pub trait OptionToResult<T, E> {
    fn into_result(self, error: E) -> Result<T, E>;
}

impl<T, E> OptionToResult<T, E> for Option<T> {
    fn into_result(self, error: E) -> Result<T, E> {
        if let Some(me) = self {
            Ok(me)
        } else {
            Err(error)
        }
    }
}

pub trait Apply<T> {
    fn apply<F>(self, action: F) -> T where F : FnMut(&mut Self) -> ();
}
impl<T> Apply<T> for T {
    fn apply<F>(self, mut action: F) -> T where F : FnMut(&mut Self) -> () {
        let mut me = self;
        action(&mut me);
        me
    }
}